OpenCV 基础教程

OpenCV 图片基本操作

读取图像

使用:'cv2.imread(文件名, 标记)' 可选的标记如下:

默认情况下是 3 通道颜色模式,OpenCV 的颜色通道排列方式为 BGR,而传统的排列方式为 RGB,如下图 BGR 方式排列:

img

png

显示图片

使用 cv2.imshow() 显示图像,

cv2.namedWindow('image',cv2.WINDOW_NORMAL) 可以在展示图片前先常见一个窗口,并对窗口设置参数

保存图片

使用 cv2.imwrite(文件名, 文件数据, flags) 保存成功返回 true, 可选的 flag 值与读取图片类似,有 :IMWRITE_JPEG_CHROMA_QUALITY 等。

png

 

 

OpenCV 视频操作

读取视频

对视频的捕获可以使用 videoCapture() ,传入 0 表示使用计算机内置的摄像头,传入文件名则读取视频。

保存视频

保存视频使用 VideoWriter() 其含有一下几个参数:

The constructors/functions initialize video writers. On Linux FFMPEG is used to write videos; on Windows FFMPEG or VFW is used; on MacOSX QTKit is used.

需要注意的是,设置的保存尺寸一定要和原始帧的大小一致,否则无法保存成视频。

【参考】

OpenCV 中的绘图函数

OpenCV 中有以下几种绘图函数:

以上函数共有的参数如下:

画线

png

画矩形

需要告诉函数左上角顶点和右下角顶点的坐标

png

画圆形

需要指定圆心坐标和半径大小,可以在上面矩形中画个圆

png

画椭圆

参数为:

png

绘制多边形

需要指定每个顶点的坐标,构建一个大小相等于行数 × 1 × 2 的数组,行数就是点的数目,这个数组必须为int32。

cv2.polylines() 可以用来画很多条线。只把想画的线放在一 个列中将个列传给函数就可以了。每条线会独立绘制。会比用 cv2.line() 一条一条的绘制快一些。

png

添加文字

需要设置,文字内容,绘制的位置,字体类型、大小、颜色、粗细、类型等,这里推荐linetype=cv2.LINE_AA。

目前 OpenCV 不支持添加中文,想要添加中文可以将图片转成 PIL 格式,在 PIL 格式上添加文字之后在转成 OpenCV 格式。

png

OpenCV 进阶操作

图片进阶操作

获取并修改像素值

读取一副图像,根据像素的 的坐标获取它的像素值,对于RGB图像而言,返回RGB的值,对于灰度图则返回灰度值

 

numpy 是经过优化了的进行快速矩阵运算的包,所以不推荐逐个获取像素值并修改能矩阵运算就不要用循环。例如前5行的后3列,用 numpyarray.item()array.itemset() 会更好。但是返回是标量,如果想获得所有RGB的值,需要使用 array.item()分割他们。 更好的方法是

查看图像的属性

属性的查看可以使用如下的方法:

ROI 操作

ROI 即 Region Of Interest,对感兴趣的区域操作。操作的实现都是通过 numpy索引来实现。

【参考】

png

 

png

 

png

通道拆分

有时需要对RGB三个通道分别操作,这就需要拆分RGB为单个通道。有时需要把独立的通道的图片合成一个RGB。

使用 split 可进行通道拆分, 但 OpenCV 拆出来的通道为 B、G、R。cv2.split() 是比较耗时的操作,能用numpy就尽量使用。假如想使所有红色通道值都为0,不必拆分再赋值,可以使用numpy索引,img[:,:,2]=0 , 这样更快.

使用 merge 可进行通道合并,注意合并的顺序。

【参考】

png

 

png

图片扩边(填充)

想为图像周围建一个边可以使用 cv2.copyMakeBorder() 函数。这经常在卷积运算或0填充时被用到。具体参数如下:

png

图像上的运算

图像加法运算

加法运算使用 cv2.add(), 也可以直接使用 numpy,res=img1+img2.OpenCV 中的加法与 Numpy 的加法有所不同的。 OpenCV 的加法是一种饱和操作,而 Numpy 的加法是一种模操作。 例如,对于 100+200,使用 cv2.add() 函数得到的是 255,而使用 Numpy 的加法得到的是 300%256 . OpenCV的结果会更好,so尽量使用OpenCV中的函数

两幅图像的大小,类型必须一致,或者第二个图像可以是一个简单的标量值。在大小不一致的情况下可以使用 ROI 进行操作。

【参考】

png

图像混合

次处是使用 cv2.addWeighted() 做权重相加。

这也是加法,不同的是两幅图像的权重不同,这会给人一种混合或者透明的感觉。图像混合的计算公式如下:

g(x) = (1−α)f0(x)+αf1(x)

通过修改α的值(0-->1),可以实现很酷的混合。例:将两幅图像混合,第一幅权重为0.7 . 第二幅权重为0.3。函数cv2.addWeighed()可以按下面的公式对图片进行混合。dst = α·img1 + β·img2+γ 。这里γ的取值为0.

png

通过 ROI 混合图像

 

png

图像的位运算

位运算包括:and、or、not、xor 四种。与数学中的概念类似,and 表示两者相同的部分,or 表示两者叠加,not 是取反的意思,xor 表示相同的部分取反。

【参考】

png

 

png

png

颜色空间转换

颜色空间装换

在 OpenCV 中有 超过150 种进行颜色空间转换的方法。但是你以后就会发现我们经常用到的也就两种:BGR ↔ Gray 和 BGR ↔ HSV。

我们用到的函数是 cv2.cvtColor(input_image,flag),其中 flag 就是转换类型。 对于BGR↔Gray的转换,我们使用的flag就是cv2.COLOR_BGR2GRAY。 同样对于 BGR↔HSV 的转换我们用的 flag 就是 cv2.COLOR_BGR2HSV

在 OpenCV 的 HSV 格式中,H(色彩/色度)的取值范围是 [0,179], S(饱和度)的取值范围 [0,255],V(亮度)的取值范围 [0,255]。但是不同的软件使用的值可能不同。所以当你拿 OpenCV 的 HSV 值与其他软件的 HSV 值对比时,一定要记得归一化。

你可以下的命令得到所有可用的 flag:

 

png

物体追踪 HSV

现在我们知怎样将一幅图像从 BGR 换到 HSV 了,我们可以利用 一点来提取带有某个特定色的物体。在 HSV 颜色空间中要比在 BGR 空间中更容易表示一个特定颜色。在我们的程序中,我们提取的是一个红色的苹果 ,可以使用 cv2.inRange() 来选定。此过程分为以下几个步奏:

追踪物体轮廓,以后可以找物体中心,然后跟踪物体,可以在摄像头前挥挥手就可以画图等一些有趣的事。

【参考】

png

几何变化

扩展缩放

只是改变图像的尺寸大小,cv2.resize() 可以实现这个功能。在缩放时推荐 cv2.INTER_AREA,在拓展时推荐 cv2.INTER_CUBIC(慢)和cv2.INTER_LINEAR。默认情况下所有改变图像尺寸大小的操作使用的是插值法都是 cv2.INTER_LINEAR

cv2.resize()有以下几个参数:

 

png

平移变换

如果想要沿(x,y)方向移动,移动的距离为(x,y)可以以下面方式构建移动矩阵,M:[[1,0,x],[0,1,y]]。可以使用Numpy数组构建矩阵,数据类型是np.float32,然后传给函数 cv2.warpAffine() , 其参数如下:

png

旋转

对一个图像旋转角度θ,需要使用下面的旋转矩阵。

img

但OpenCVC允许在任意地方进行旋转,所以矩阵应该为

img

其中 α = scale · cos θ. 为构建旋转矩阵,OpenCV提供了一个函数 cv2.getRotationMatrix2D 其参数如下:

png

仿射变换

仿射变换就是图像的线性变换加上平移,用一幅图表示,就是

img

由 image1 到 image2 的转换经过了三个操作

在仿射变换中,原图中所有平行线在结果图像中同样平行。为创建这个矩阵,需要从原图像中找到三个点以及他们在输出图像中的位置,然后cv2.getAffineTransForm() 会创建一个2X3的矩阵。最后这个矩阵会被传给函数 cv2.warpAffine()

【参考】

png

透视变换

透视变换绝对是一项很酷的功能。我们经常会用手机去拍身份证和文件,无论你怎么拍,貌似都拍不正或者有边框。如果你使用过手机上面一些扫描类软件,比如”扫描全能王“,”Office Lens“,它们能很好地矫正图片。这些软件就是应用透视变换实现的,跟仿射变换一样,我们不用知道它的具体原理。

透视变换后,原图中的直线依旧是直线。如下图,我们实现这个功能:

image

上图,首先确定四个角的坐标,并确定变换后的宽高,然后对每个点进行变换,先确定一个变换的基点,然后通过宽高算出其他几个点应该在的位置。

对于视角变换,我们需要一个3x3变换矩阵。在变换前后直线还是直线。需要在原图上找到4个点,以及他们在输出图上对应的位置,这四个点中任意三个都不能共线,可以有函数 cv2.getPerspectiveTransform() 构建,然后这个矩阵传给函数 cv2.warpPerspective().

png

图像阈值

此处会用到 cv2.threshold, cv2.adaptiveThreshold 两个函数

固定阈值

当像素值高于阀值时,我们给这个像素赋予一个新值(可能是白色),否则我们给它赋予另外一种颜色(也许是黑色)。这个函数就是 cv2.threshold()参数如下:

【参考】

png

结合上图,可以看到五种方式的行为描述如下:

img

自适应阈值

根据图像上的每一个小区域计算与其对应的阀值。因此在同一幅图像上的不同区域采用的是不同的阀值,从而使我们能在亮度不同的情况下得到更好的结果。

这里需要使用 cv2.adaptiveThreshold 函数,参数如下:

png

Otsu阈值法

在前面固定阈值中,我们是随便选了一个阈值如127,那如何知道我们选的这个阈值效果好不好呢?答案是:不断尝试,所以这种方法在很多文献中都被称为经验阈值。Otsu阈值法就提供了一种自动高效的二值化方法 ,并且Otsu’s非常适合于图像灰度直方图具有双峰的情况,他会在双峰之间找到一个值作为阈值,对于非双峰图像,可能并不是很好用。那么经过Otsu’s得到的那个阈值就是函数 cv2.threshold 的第一个参数了。因为Otsu’s方法会产生一个阈值,那么函数 cv2.threshold 的的第二个参数(设置阈值)就是0了,并且在 cv2.threshold 的方法参数中还得加上语句 cv2.THRESH_OTSU

【参考】

png

参考